package web.sms.mvc.service.impl;

import cn.palerock.core.shiro.useradmin.auditor.UserAuditor;
import cn.palerock.utils.StringUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import web.sms.core.exception.AttributeCheckFailureException;
import web.sms.core.exception.ServiceException;
import web.sms.core.exception.shiro.ServiceExceptionCallback;
import web.sms.core.pojo.ErrorInfo;
import web.sms.mvc.constant.UserConstant;
import web.sms.mvc.constant.WorkerGroupConstant;
import web.sms.mvc.dao.RoleMapper;
import web.sms.mvc.dao.UserMapper;
import web.sms.mvc.entity.Role;
import web.sms.mvc.entity.User;
import web.sms.mvc.service.UserService;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * 时间： 2017/12/12
 *
 * @author Eoly
 */
@Service
public class UserServiceImpl implements UserService {

    private final Logger log = LoggerFactory.getLogger(UserService.class);
    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final UserAuditor userAuditor;

    @Autowired
    public UserServiceImpl(UserMapper userMapper, RoleMapper roleMapper, UserAuditor userAuditor) {
        this.userMapper = userMapper;
        this.roleMapper = roleMapper;
        this.userAuditor = userAuditor;
    }

    @Override
    public User doLogin(String username, String password) {
        return doLogin(username, password, false);
    }

    @Override
    public User doLogin(String username, String password, Boolean rememberMe) {
        rememberMe = rememberMe == null ? false : rememberMe;
        log.debug("rememberMe:" + rememberMe);
        UsernamePasswordToken token = new UsernamePasswordToken(
                username,
                password,
                rememberMe
        );
        try {
            // 登录动作
            SecurityUtils.getSubject().login(token);
        } catch (UnknownAccountException e) {
            // 用户不存在
            throw new ServiceException(UserConstant.USERNAME_OR_PASSWORD_ERROR);
        } catch (LockedAccountException e) {
            // 用户冻结
            throw new ServiceException(UserConstant.USER_LOCKED_ERROR);
        } catch (IncorrectCredentialsException e) {
            // 密码错误
            throw new ServiceException(UserConstant.USERNAME_OR_PASSWORD_ERROR);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            // 其他异常
            throw new ServiceException(UserConstant.LOGIN_FAILURE_ERROR);
        }
        // 保存用户信息到session
        User userLogged = userMapper.selectAuthenticationByLoginName(username);
        SecurityUtils.getSubject().getSession().setAttribute("userLogged", userLogged);
        return userLogged;
    }

    @Override
    public void doLogout() {
        Subject subject = SecurityUtils.getSubject();
        // 判断用户是否登录
        if (!subject.isAuthenticated() && !subject.isRemembered()) {
            throw new ServiceException(UserConstant.NOT_LOGIN_ERROR);
        }
        SecurityUtils.getSubject().logout();
    }

    @Override
    public void addUser(User user) {
        // 检查权限
        userAuditor.checkPerms("user:admin|user:add", new ServiceExceptionCallback());
        // 清空不能添加的属性
        emptyUserProperties(user, "userStatus", "userId");
        // 检查用户及用户属性
        checkUserProperties(user, "userLoginName", "userPassword", "userRole", "userRealName");
        // 检查用户角色是否存在
        Role role = roleMapper.selectByPrimaryKey(user.getUserRole());
        if (role == null) {
            throw new ServiceException(UserConstant.NOT_EXIST_ROLE_ERROR, user.getUserRole());
        }
        // 超级管理员不能被添加
        if ("admin".equals(role.getRoleCode())) {
            throw new ServiceException(UserConstant.CAN_NOT_CREATE_ADMIN_USER);
        }
        // 密码的加密处理
        String MD5Password = new Md5Hash(user.getUserPassword()).toString();
        user.setUserPassword(MD5Password);
        // 添加用户
        int result = userMapper.insertSelective(user);
        if (result < 1) {
            // 结果改变数小于1即添加失败
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.ADDING_USER_FAILURE);
        }
    }

    @Override
    public void editUser(User user) {
        // 检查用户及用户id是否存在
        checkUserProperties(user, "userId");
        // 检查权限,设定若操作用户为欲修改用户本身则通过
        userAuditor.checkPerms("user:admin|user:edit", user.getUserId(),
                new ServiceExceptionCallback());
        // 获取当前操作用户的id
        Integer currentUserId = (Integer) userAuditor.getCurrentSubjectUniqueKey(
                new ServiceExceptionCallback());
        if (currentUserId.equals(user.getUserId())) {
            // 设定用户自己操作时不能改变的属性
            emptyUserProperties(user,
                    "userLoginName", "userOpenId",
                    "userRole", "userStatus", "userPassword");
        } else {
            // 设定管理员操作时不能改变的属性
            emptyUserProperties(user,
                    "userLoginName", "userOpenId", "userStatus", "userPassword");
            // 检查用户角色是否存在
            Role role = roleMapper.selectByPrimaryKey(user.getUserRole());
            if (role == null) {
                throw new ServiceException(UserConstant.NOT_EXIST_ROLE_ERROR, user.getUserRole());
            }
            // 超级管理员不能被添加
            if ("admin".equals(role.getRoleCode())) {
                emptyUserProperties(user, "userRole");
                log.debug(UserConstant.CAN_NOT_TURN_TO_ADMIN_USER.getMsg());
            }
        }
        // 超级管理员的角色不能被修改
        if (hasRole(user.getUserId(), "admin")) {
            emptyUserProperties(user, "userRole");
            log.debug(UserConstant.CAN_NOT_MODIFY_ROLE_OF_ADMIN_USER.getMsg());
        }

        // 修改用户属性
        int result = userMapper.updateByPrimaryKeySelective(user);
        if (result < 1) {
            // 结果改变数小于1即修改失败
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.EDITING_USER_FAILURE);
        }
    }

    @Override
    public void editPassword(Integer userId, String oldPassword, String newPassword) {
        if (userId == null) {
            // 若修改当前用户密码则获取用户id
            userId = (Integer) userAuditor.getCurrentSubjectUniqueKey(new ServiceExceptionCallback());
        } else {
            // 检查权限,设定若操作用户为欲修改用户本身则通过
            userAuditor.checkPerms("user:admin|user:edit", userId,
                    new ServiceExceptionCallback());
        }
        // 检查新密码是否为空
        if (newPassword == null || "".equals(newPassword)) {
            throw new ServiceException(
                    UserConstant.FAIL_TO_EDIT_USER_PASSWORD_ERROR,
                    UserConstant.NEW_PASSWORD_IS_NULL
            );
        }
        // 获取当前操作用户的id
        Integer currentUserId = (Integer) userAuditor.getCurrentSubjectUniqueKey(
                new ServiceExceptionCallback());
        User editedUser = userMapper.selectAuthenticationByPrimaryKey(userId);
        boolean isNeedToCheckOldPassword = true;
        // 检查用户是否存在
        try {
            checkUserProperties(editedUser, "userPassword");
        } catch (AttributeCheckFailureException e) {
            // 若用户密码为空不用检查旧密码是否对应
            if ("userPassword".equals(e.getAttributeName())) {
                isNeedToCheckOldPassword = false;
            }
        }
        // 若操作人员为管理员不用检查旧密码是否对应
        if (!currentUserId.equals(userId)) {
            isNeedToCheckOldPassword = false;
        }
        // 检查旧密码是否对应
        if (isNeedToCheckOldPassword) {
            String passwordBefore = editedUser.getUserPassword();
            if (oldPassword == null) {
                throw new ServiceException(
                        UserConstant.FAIL_TO_EDIT_USER_PASSWORD_ERROR,
                        UserConstant.OLD_PASSWORD_IS_NULL);
            }
            String md5Password = new Md5Hash(oldPassword).toString();
            if (!md5Password.equals(passwordBefore)) {
                throw new ServiceException(
                        UserConstant.FAIL_TO_EDIT_USER_PASSWORD_ERROR,
                        UserConstant.CAN_NOT_MATCH_ODL_PASSWORD
                );
            }
        }
        // 修改密码操作
        String md5Password = new Md5Hash(newPassword).toString();
        editedUser.setUserPassword(md5Password);
        int result = userMapper.updateByPrimaryKeySelective(editedUser);
        if (result < 1) {
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.EDITING_USER_PASSWORD_FAILURE);
        }
    }

    @Override
    public void doFreezeUser(Integer userId) {
        // 检查权限
        userAuditor.checkPerms("user:admin|user:edit",
                new ServiceExceptionCallback());
        // 获取待冻结的用户
        User editedUser = userMapper.selectAuthenticationByPrimaryKey(userId);
        checkUserProperties(editedUser, "userRole");
        // 不能冻结超级管理员
        if ("admin".equals(editedUser.getUserRole())) {
            throw new ServiceException(
                    UserConstant.FREEZING_USER_FAIL_ERROR,
                    UserConstant.ADMIN_USER_IS_INOPERABLE
            );
        }
        // 欲冻结的用户已处于冻结状态或已删除
        if (editedUser.getUserStatus() != null &&
                editedUser.getUserStatus() < User.NORMAL_USER_STATUS) {
            throw new ServiceException(
                    UserConstant.FREEZING_USER_FAIL_ERROR,
                    UserConstant.USER_IS_FREEZING_OR_NOT_EXIST
            );
        }
        // 冻结操作
        editedUser.setUserStatus(User.FREEZING_USER_STATUS);
        int result = userMapper.updateByPrimaryKeySelective(editedUser);
        if (result < 1) {
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.FREEZING_USER_FAILURE);
        }
    }

    @Override
    public void doUnfreezeUser(Integer userId) {
        // 检查权限
        userAuditor.checkPerms("user:admin|user:edit",
                new ServiceExceptionCallback());
        // 获取待冻结的用户
        User editedUser = userMapper.selectAuthenticationByPrimaryKey(userId);
        checkUserProperties(editedUser);
        // 欲冻结的用户已处于冻结状态或已删除
        if (editedUser.getUserStatus() != null &&
                (editedUser.getUserStatus().equals(User.NORMAL_USER_STATUS)
                        || editedUser.getUserStatus().equals(User.NOT_EXIST_USER_STATUS)
                )) {
            throw new ServiceException(
                    UserConstant.UNFREEZING_USER_FAIL_ERROR,
                    UserConstant.USER_IS_UNFREEZING_OR_NOT_EXIST
            );
        }
        // 冻结操作
        editedUser.setUserStatus(User.NORMAL_USER_STATUS);
        int result = userMapper.updateByPrimaryKeySelective(editedUser);
        if (result < 1) {
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.UNFREEZING_USER_FAILURE);
        }
    }

    @Override
    public void deleteUser(Integer userId) {
        // 检查权限
        userAuditor.checkPerms("user:admin|user:edit|user:delete",
                new ServiceExceptionCallback());
        // 不能删除管理员用户
        User user = userMapper.selectAuthenticationByPrimaryKey(userId);
        checkUserProperties(user);
        if (user.getUserRole() != null && user.getUserRole().equals("admin")) {
            throw new ServiceException(
                    UserConstant.DELETING_USER_FAIL_ERROR,
                    UserConstant.ADMIN_USER_IS_INOPERABLE
            );
        }
        user.setUserStatus(User.NOT_EXIST_USER_STATUS);
        int result = userMapper.updateByPrimaryKeySelective(user);
        if (result < 0) {
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.DELETE_USER_FAILURE);
        }
        // 删除绑定的微信id
        if (user.getUserOpenId() == null) {
            return;
        }
        int res = userMapper.updateAttributesToNull(user.getUserId(), "user_open_id");
        if (res < 0) {
            throw new ServiceException(
                    UserConstant.SERVER_RUN_SQL_FAILURE,
                    UserConstant.DELETE_USER_FAILURE);
        }
    }

    @Override
    public Boolean isLoginNameExist(String loginName) {
        Integer userId = userMapper.selectUserIdByUserLoginName(loginName);
        return userId != null;
    }

    @Override
    public User findUserWithBaseInfo(Integer userId) {
        User user = userMapper.selectUserBaseInfoByUserId(userId);
        checkUserProperties(user);
        return user;
    }

    @Override
    public User findUserWithAuthenticationInfo(String loginName) {
        User user = userMapper.selectAuthenticationByLoginName(loginName);
        checkUserProperties(user);
        return user;
    }

    @Override
    public User findUserWithAuthenticationInfo(Integer userId) {
        User user = userMapper.selectAuthenticationByPrimaryKey(userId);
        checkUserProperties(user);
        return user;
    }

    @Override
    public List<User> findUserByAttributes(User checkUser) {
        // 检查权限
        userAuditor.checkPerms("user:admin|user:find",
                new ServiceExceptionCallback());
        List<User> users = userMapper.selectAllUserByAttributes(checkUser, null);
        if (users == null) {
            throw new ServiceException(UserConstant.NOT_FIND_MATCH_USER_ERROR);
        }
        return users;
    }

    @Override
    public PageInfo<User> findUsersByPage(Integer pageNumber, Integer pageSize) {
        // 检查权限
        userAuditor.checkPerms("user:admin|user:find",
                new ServiceExceptionCallback());
        PageHelper.startPage(pageNumber, pageSize);
        List<User> users = userMapper.selectAllUsers();
        if (users == null || users.size() == 0) {
            throw new ServiceException(
                    UserConstant.EMPTY_USERS_ERROR
            );
        }
        return new PageInfo<User>(users);
    }

    @Override
    public PageInfo<User> findUsersByPageAndNameAndRole(Integer pageNumber, Integer pageSize, String userRealName, String userRole) {
        userAuditor.checkPerms("user:admin|user:find",
                new ServiceExceptionCallback());
        User checkUser = new User();
        if (userRealName != null && !"".equals(userRealName) && !"%%".equals(userRealName)) {
            checkUser.setUserRealName(userRealName);
        }
        if (userRole != null && !"".equals(userRole)) {
            checkUser.setUserRole(userRole);
        }
        userAuditor.checkPerms("user:admin|user:find",
                new ServiceExceptionCallback());
        PageHelper.startPage(pageNumber, pageSize);
        List<User> users = userMapper.selectAllUserByAttributes(checkUser, null);
        if (users == null) {
            throw new ServiceException(UserConstant.NOT_FIND_MATCH_USER_ERROR);
        }
        return new PageInfo<User>(users);
    }

    @Override
    public Integer findAllUserCount() {
        Integer count = userMapper.selectAllUsersCount();
        if (count == null || count < 0) {
            count = 0;
        }
        return count;
    }

    @Override
    public Boolean hasRole(Integer userId, String roleCode) {
        if (roleCode == null) {
            return false;
        }
        User user = userMapper.selectAuthenticationByPrimaryKey(userId);
        checkUserProperties(user);
        if (user.getUserStatus() < User.NORMAL_USER_STATUS) {
            return false;
        }
        roleCode = roleCode.replaceAll(" ", "");
        String[] roles = roleCode.split("\\|");
        for (String r : roles) {
            if (r.equals(user.getUserRole())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Boolean hasRole(Integer userId, String roleCode, ErrorInfo errorInfo) {
        Boolean hasUserRole = false;
        try {
            hasUserRole = hasRole(userId, roleCode);
        } catch (AttributeCheckFailureException e) {
            if ("null".equals(e.getAttributeName())) {
                throw new ServiceException(errorInfo);
            }
        }
        return hasUserRole;
    }

    @Override
    public User getLoggedUser() {
        User user = (User) userAuditor.getSession().getAttribute("userLogged");
        try {
            checkUserProperties(user, "userRole", "userId");
        } catch (AttributeCheckFailureException e) {
            throw new ServiceException(UserConstant.NOT_LOGIN_ERROR);
        }
        return user;
    }

    @Override
    public User findUserByOpenId(String openid) {
        User user = new User();
        user.setUserOpenId(openid);
        List<User> users = userMapper.selectAllUserByAttributes(user, null);
        if (users.size() < 1) {
            return null;
        }
        return users.get(0);
    }

    /**
     * 检查用户及用户属性是否为空
     *
     * @param user       用户实体
     * @param properties 用户属性集合
     */
    public static void checkUserProperties(User user, String... properties) {
        if (user == null) {
            throw new AttributeCheckFailureException(UserConstant.USER_NOT_EXIST_ERROR, "null");
        }
        for (String attributeName : properties) {
            String methodName = "get" + StringUtils.captureName(attributeName);
            try {
                Object obj = User.class.getMethod(methodName).invoke(user);
                if (obj == null) {
                    throw new AttributeCheckFailureException(
                            UserConstant.NULL_USER_PROPERTY_ERROR, attributeName,
                            new String[]{attributeName});
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 令指定用户属性值设置为空
     *
     * @param user       用户实体
     * @param properties 用户属性集
     */
    public static void emptyUserProperties(User user, String... properties) {
        if (user == null) {
            throw new AttributeCheckFailureException(UserConstant.USER_NOT_EXIST_ERROR, "null");
        }
        for (String attributeName : properties) {
            String methodName = "set" + StringUtils.captureName(attributeName);
            Method[] methods = User.class.getMethods();
            for (Method method : methods) {
                if (methodName.equals(method.getName())) {
                    try {
                        method.invoke(user, (Object) null);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}
